// -------------------------------------------------
// BlinKit - BlinKit Library
// -------------------------------------------------
//   File Name: web_page_host_window.cpp
// Description: WebPageHostWindow Class
//      Author: Ziming Li
//     Created: 2022-05-02
// -------------------------------------------------
// Copyright (C) 2022 MingYang Software Technology.
// -------------------------------------------------

#include "./web_page_host_window.h"

#include <windowsx.h>
#include "blinkit/app/app_impl.h"
#include "blinkit/ui/compositor/compositor.h"
#include "blinkit/ui/web_page.h"
#include "blinkit/win/tooltip.h"

namespace BlinKit {

#ifndef NDEBUG
WebPageHostWindow::MessageLogger::MessageLogger(const UINT msg) : m_msg(msg)
{
    BKLOG("ProcessWindowMessage begin: 0x%04X at %d", m_msg, GetTickCount());
}

WebPageHostWindow::MessageLogger::~MessageLogger(void)
{
    BKLOG("ProcessWindowMessage end: 0x%04X at %d", m_msg, GetTickCount());
}
#endif

WebPageHostWindow::WebPageHostWindow(WebPage *page, HWND hWnd)
    : WebPageHost(page)
    , m_paintSession(*this)
{
    if (nullptr != hWnd)
        AttachHWND(hWnd);
}

WebPageHostWindow::~WebPageHostWindow(void)
{
    AppImpl::Get().GetCompositor().Detach(this);
}

void WebPageHostWindow::AttachHWND(HWND hWnd)
{
    ASSERT(nullptr != hWnd);
    ASSERT(nullptr == m_hWnd);
    m_hWnd = hWnd;

    if (GetClassLong(m_hWnd, GCL_STYLE) & CS_DBLCLKS)
        m_mouseSession.EnableDoubleClick();
}

void WebPageHostWindow::BitBlt(HDC hMemoryDC, const IntRect &rect)
{
    HDC hdc = GetDC(m_hWnd);
    ::BitBlt(hdc, rect.x(), rect.y(), rect.width(), rect.height(), hMemoryDC, rect.x(), rect.y(), SRCCOPY);
    ReleaseDC(m_hWnd, hdc);
}

void WebPageHostWindow::FlushFrame(const SkBitmap &bitmap, const IntPoint &position, const IntSize &size, const SkPaint &paint)
{
    m_paintSession.FlushFrame(bitmap, IntRect(position, size), paint);
}

void WebPageHostWindow::HideTooltip(void)
{
    if (m_tooltip)
        m_tooltip->Hide();
}

void WebPageHostWindow::OnActivate(HWND hwnd, UINT state, HWND hwndActDeact, BOOL fMinimized)
{
    if (WA_INACTIVE == state)
        HideTooltip();
}

void WebPageHostWindow::OnMouse(UINT message, WPARAM wParam, LPARAM lParam)
{
    if (m_changingSizeOrPosition)
        return;

    const auto callback = [webPage = GetWebPage()](const WebMouseEvent &e) {
        webPage->ProcessInput(e);
    };
    m_mouseSession.Process(*this, message, wParam, lParam, callback);
}

LRESULT WebPageHostWindow::OnNotify(HWND hwnd, int id, LPNMHDR nmhdr)
{
    switch (nmhdr->code)
    {
        case TTN_SHOW:
            if (m_tooltip && m_tooltip->ProcessShowNotify(nmhdr->hwndFrom))
                return TRUE;
            break;
    }
    return 0;
}

void WebPageHostWindow::OnPaint(HWND hwnd)
{
    ASSERT(m_hWnd == hwnd);

    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd, &ps);

    int x = ps.rcPaint.left;
    int y = ps.rcPaint.top;
    int cx = ps.rcPaint.right - ps.rcPaint.top;
    int cy = ps.rcPaint.bottom - ps.rcPaint.top;
    m_paintSession.BitBlt(hdc, x, y, cx, cy);

    EndPaint(hwnd, &ps);
}

void WebPageHostWindow::OnShowWindow(HWND hwnd, BOOL fShow, UINT status)
{
    if (!fShow && m_tooltip)
        m_tooltip->Hide();
}

bool WebPageHostWindow::ProcessMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT &result)
{
    bool processed = true;
    switch (Msg)
    {
        case WM_ERASEBKGND:
            result = FALSE;
            break;
        case WM_SETCURSOR:
            processed = m_cursorSession.ProcessSetCursorMessage(wParam, lParam, result);
            break;
        case WM_PAINT:
            HANDLE_WM_PAINT(hWnd, wParam, lParam, OnPaint);
            break;

        case WM_LBUTTONDOWN:
        case WM_LBUTTONUP:
        case WM_MOUSEMOVE:
        case WM_RBUTTONDOWN:
        case WM_RBUTTONUP:
        case WM_MBUTTONDOWN:
        case WM_MBUTTONUP:
        case WM_MOUSELEAVE:
            OnMouse(Msg, wParam, lParam);
            break;

        case WM_ACTIVATE:
            HANDLE_WM_ACTIVATE(hWnd, wParam, lParam, OnActivate);
            break;

        case WM_ENTERSIZEMOVE:
            m_changingSizeOrPosition = true;
            break;
        case WM_EXITSIZEMOVE:
            m_changingSizeOrPosition = false;
            break;

        case WM_NOTIFY:
            result = HANDLE_WM_NOTIFY(hWnd, wParam, lParam, OnNotify);
            break;

        case WM_SHOWWINDOW:
            HANDLE_WM_SHOWWINDOW(hWnd, wParam, lParam, OnShowWindow);
            break;

        default:
            processed = false;
    }
    return processed;
}

void WebPageHostWindow::ShowToolTip(const std::string &text, TextDirection dir, unsigned long cookie)
{
    if (!m_tooltip)
        m_tooltip = std::make_unique<Tooltip>(m_hWnd);
    m_tooltip->Show(zed::multi_byte_to_wide_string(text), dir, cookie);
}

void WebPageHostWindow::SwapFrame(std::unique_ptr<AnimationFrame> &frame, const IntSize &size)
{
    m_paintSession.SwapFrame(frame, size);
}

void WebPageHostWindow::UpdateCursor(const WebCursorInfo &cursorInfo)
{
    m_cursorSession.Update(cursorInfo);
}

} // namespace BlinKit
